﻿/*
 * Copyright 2005-2011 Edward L. Stauff.                  Contact: <EdStauff@gmail.com>.
 * 
 * This file, "XmlElementContext.cs", is part of the "Stauffware.Xml" library.
 * "Stauffware.Xml" is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by the 
 * Free Software Foundation, either version 3 of the License, or (at your 
 * option) any later version.
 * 
 * "Stauffware.Xml" is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser Public License along  
 * with "Stauffware.Xml".  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using Stauffware.Common.Core;

namespace Stauffware.Common.Xml
{
	//#################################################################################
	/// <summary>
	/// Describes one level of XML context.  For internal use only.
	/// </summary>

	public class XmlElementContext
	{
		/// <summary> The name of the node. </summary>
		public readonly string Name;
		/// <summary> The nesting level of the node. </summary>
		public readonly int NodeDepth;
		/// <summary> The line number, or -1 if unknown. </summary>
		public readonly int LineNumber;
		/// <summary> The position within the line, or -1 if unknown. </summary>
		public readonly int LinePosition;

		/// <summary>
		/// Constructs an XmlElementContext that captures the current position of the
		/// given NiceXmlReader.
		/// </summary>
		/// <param name="rdr"></param>
		public XmlElementContext (NiceXmlReader rdr)
		{
			Require.Condition(rdr.NodeType == XmlNodeType.Element);
			this.Name = rdr.Name;
			this.NodeDepth = rdr.Depth;
			this.LineNumber = rdr.LineNumber;
			this.LinePosition = rdr.LinePosition;
		}
	}

	//#################################################################################
	/// <summary>
	/// Maintains a stack of XmlContext objects, representing a logical location in
	/// an XML document.
	/// </summary>

	public class XmlElementContextStack
	{
		private readonly List<XmlElementContext> List = new List<XmlElementContext>();

		/// <summary>
		/// Gets the number of contexts currently on the stack.
		/// </summary>
		public int Depth { get { return this.List.Count; } }

		/// <summary>
		/// Gets the current (topmost) context.
		/// </summary>
		public XmlElementContext Top
		{
			get { return this.List[this.List.Count - 1]; }
		}

		/// <summary>
		/// Pushes the current context of the given reader.
		/// </summary>
		public void Push (NiceXmlReader rdr)
		{
			this.List.Add(new XmlElementContext(rdr));
		}

		/// <summary>
		/// Pops a context off the stack.
		/// </summary>
		public void Pop ()
		{
			Require.Condition(!this.IsEmpty);
			this.List.RemoveAt(this.List.Count - 1);
		}

		/// <summary>
		/// Returns the Nth item from the top of the stack (0 = topmost).
		/// </summary>
		public XmlElementContext GetFromTop (int n)
		{
			return this.List[this.List.Count - (n + 1)];
		}

		/// <summary>
		/// Gets whether the stack is empty.
		/// </summary>
		public bool IsEmpty { get { return this.List.Count == 0; }	}

		/// <summary>
		/// Returns a string representation of the context stack.
		/// </summary>
		/// <param name="separator">what to put between context levels</param>

		public string ToString (string separator)
		{
			Require.NotNull(separator);
			if (this.IsEmpty)
				return "";

			string str = "";
			foreach (XmlElementContext xec in this.List)
				str += separator + xec.Name;
			return str.Substring(separator.Length);
		}
	}

	//#################################################################################
}
